﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace DesertKharonIrcPlugin.GdiPlusRendering
{
    public abstract class DrawableBase : IDisposable
    {
        protected Stopwatch _ageStopwatch;
        protected double _lifeTimeInSeconds = 60d;
        protected double _fadeOutTimeInSeconds = 2d;
        protected double _fadeOutStartAtAge = 58d;

        protected Bitmap _bitmap;
        protected BitmapData _bitmapData;
        protected byte[] _bitmapBytes;
        protected Rectangle _fullBitmapRect;
        protected float _x;
        protected float _y;
        protected int _width;
        protected int _height;
        protected float? _opacity;

        protected float? _lastOpacity = null;
        protected Bitmap _changedBitmap = null;
        protected byte[] _changedBitmapBytes = null;

        private int _lineNumber = 0;
        private bool _newLine = false;

        private string _authorNickname;

        public float X
        {
            get { return _x; }
            set { _x = value; }
        }

        public float Y
        {
            get { return _y; }
            set { _y = value; }
        }

        public float Width
        {
            get { return _width; }
        }

        public float Height
        {
            get { return _height; }
        }

        public float? Opacity
        {
            get { return _opacity; }
            set { _opacity = value; }
        }

        public bool IsDead
        {
            get { return _ageStopwatch.Elapsed.TotalSeconds > _lifeTimeInSeconds; }
        }

        public int LineNumber
        {
            get { return _lineNumber; }
            set { _lineNumber = value; }
        }

        public bool NewLine
        {
            get { return _newLine; }
            set { _newLine = value; }
        }

        public string AuthorNickname
        {
            get { return _authorNickname; }
        }

        public double LifeTimeInSeconds
        {
            get { return _lifeTimeInSeconds; }
            set
            {
                _lifeTimeInSeconds = Math.Max(value, _fadeOutTimeInSeconds);
                _fadeOutStartAtAge = Math.Max(_lifeTimeInSeconds - _fadeOutTimeInSeconds, 0f);
            }
        }

        public double FadeOutTimeInSeconds
        {
            get { return _fadeOutTimeInSeconds; }
            set
            {
                _fadeOutTimeInSeconds = Math.Max(value, 0f);
                _fadeOutStartAtAge = Math.Max(_lifeTimeInSeconds - _fadeOutTimeInSeconds, 0f);
                _lifeTimeInSeconds = _fadeOutStartAtAge + _fadeOutTimeInSeconds;
            }
        }

        public DrawableBase(string authorNickname)
        {
            _ageStopwatch = Stopwatch.StartNew();
            _authorNickname = authorNickname;
        }

        protected void SetBitmap(Bitmap bitmap, bool disposePrevious)
        {
            if (disposePrevious && _bitmap != null)
                _bitmap.Dispose();

            _bitmap = bitmap;
            _width = _bitmap.Width;
            _height = _bitmap.Height;
            _fullBitmapRect = new Rectangle(0, 0, _width, _height);
            _bitmapBytes = new byte[_width * _height * 4];

            RecreateChangeBitmap();
        }

        protected void RecreateChangeBitmap()
        {
            if (_changedBitmap != null)
                _changedBitmap.Dispose();

            _changedBitmap = new Bitmap(_width, _height);
            _changedBitmapBytes = new byte[_width * _height * 4];

            _bitmapData = _bitmap.LockBits(_fullBitmapRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
            Marshal.Copy(_bitmapData.Scan0, _bitmapBytes, 0, _bitmapData.Stride * _bitmapData.Height);
            Marshal.Copy(_bitmapData.Scan0, _changedBitmapBytes, 0, _bitmapData.Stride * _bitmapData.Height);
            _bitmap.UnlockBits(_bitmapData);
        }

        protected abstract void UpdateBitmaps();

        public void Draw(Graphics graphics)
        {
            UpdateBitmaps();

            if (_ageStopwatch.Elapsed.TotalSeconds > _fadeOutStartAtAge)
            {
                _opacity = 1f - (float)Math.Pow((_ageStopwatch.Elapsed.TotalSeconds - _fadeOutStartAtAge) / _fadeOutTimeInSeconds, 2d);
                if (_opacity.Value < 0f) _opacity = 0f;
                if (_opacity.Value > 1f) _opacity = 1f;
            }

            if (Opacity.HasValue)
            {
                if (_lastOpacity.HasValue && _lastOpacity.Value == _opacity.Value)
                    graphics.DrawImageUnscaled(_changedBitmap, (int)X, (int)Y);
                else
                {
                    _lastOpacity = _opacity;

                    for (int i = 0; i < _width * _height * 4; i += 4)
                        _changedBitmapBytes[i + 3] = (byte)((float)_bitmapBytes[i + 3] * _opacity.Value);

                    BitmapData changedBitmapData = _changedBitmap.LockBits(_fullBitmapRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
                    Marshal.Copy(_changedBitmapBytes, 0, changedBitmapData.Scan0, changedBitmapData.Stride * changedBitmapData.Height);
                    _changedBitmap.UnlockBits(changedBitmapData);

                    graphics.DrawImageUnscaled(_changedBitmap, (int)X, (int)Y);
                }
            }
            else
                graphics.DrawImageUnscaled(_bitmap, (int)X, (int)Y);
        }

        public virtual void Dispose()
        {
            if (_changedBitmap != null)
            {
                _changedBitmap.Dispose();
                _changedBitmap = null;
            }
        }

        public override string ToString()
        {
            return String.Format("LN: {0}, {1}", _lineNumber, _fullBitmapRect.ToString());
        }
    }
}
